﻿//==============================================================================  
//Copyright (C) 2012-2015 9UN.ORG. All rights reserved. 
//GUID：46172595-6b69-4526-b9da-426d663f5b05
//CLR Version: 4.0.30319.18033
//Code Author：Kevin Wang
//Contact：Email(Admin@9un.org),QQ(265382 or 74344)
//Filename：MediaLibraryFactory
//Namespace：RuanRang.Media.Core
//Functions：MediaLibraryFactory  
//Created by Kevin Wang at 2013/2/2 23:05:09 http://blog.9un.org
//============================================================================== 
#region

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using RuanRang.Media.Core.Common;
using RuanRang.Media.Core.Exceptions;
using RuanRang.Media.Core.Internal;
using RuanRang.Media.Core.Internal.DataStructures;
using RuanRang.Media.Core.Internal.Interop;
using RuanRang.Media.Core.Io;
using RuanRang.Media.Core.Media;

#endregion

namespace RuanRang.Media.Core
{
    public abstract class BaseMediaPlayer : IMediaPlayer, IDisposable
    {
        /// <summary>
        /// Gets or sets the media player event manager.
        /// </summary>
        /// <value>
        /// The media player event manager.
        /// </value>
        private IntPtr _myEventManagerHandle;

        /// <summary>
        /// The _my event callback handle
        /// </summary>
        private GCHandle _myEventCallbackHandle;

        /// <summary>
        /// The _my event callback
        /// </summary>
        private LibVlcCoreInterop.VlcEventHandlerDelegate _myEventCallback;

        /// <summary>
        ///     Initializes a new instance of the <see cref="BaseMediaPlayer" /> class.
        /// </summary>
        /// <param name="libVlcFactory">The lib VLC factory.</param>
        internal BaseMediaPlayer(ILibVlcFactory libVlcFactory)
        {
            LibVlcFactoryInstance = libVlcFactory;
            Handle = libVlcFactory.CreateLibVlcMediaPlayerHandle();
            EventsHelper.ExecuteRaiseEventDelegate =
                delegate(Delegate singleInvoke, object sender, object arg)
                {
                    var syncInvoke = singleInvoke.Target as ISynchronizeInvoke;
                    if (syncInvoke == null)
                    {
                        singleInvoke.DynamicInvoke(new[] { sender, arg });
                        return;
                    }
                    try
                    {
                        if (syncInvoke.InvokeRequired)
                            syncInvoke.Invoke(singleInvoke, new[] { sender, arg });
                        else
                            singleInvoke.DynamicInvoke(sender, arg);
                    }
                    catch (ObjectDisposedException)
                    {
                        //Because IsDisposed was true and IsDisposed could be false now...
                    }
                };
            EventsHelper.CanRaiseEvent = true;
            InitEvents();
        }

        #region Display

        /// <summary>
        ///     Sets the play window.
        /// </summary>
        /// <param name="output">The output.</param>
        public void SetPlayWindow(PlayerOutput output)
        {
            if (!PlayerOutput.IsWindowDefined) return;
            var doubleWindowBase = PlayerOutput.Window as DoubleWindowBase;
            if (doubleWindowBase != null)
            {
                // support old code
                SetDisplayOutputHwnd(doubleWindowBase.GetActiveWindowHandleInternal());
            }
            else
            {
                var nativeMediaWindow = PlayerOutput.NativeWindow as VlcNativeMediaWindow;
                if (nativeMediaWindow != null)
                {
                    // this will tell what to method we should call to initialize window
                    var window = nativeMediaWindow;
                    switch (window.WindowType)
                    {
                        case VlcWindowType.HWND:
                            SetDisplayOutputHwnd(window.NativeWindowHandle);
                            break;
                        case VlcWindowType.NSObject:
                            SetDisplayOutputNSObject(window.NativeWindowHandle);
                            break;
                        case VlcWindowType.XWindow:
                            SetDisplayOutputXWindow(window.NativeWindowHandle);
                            break;
                        case VlcWindowType.Agl:
                            SetDisplayOutputAgl(window.NativeWindowHandle);
                            break;
                    }
                }
            }
        }

        /// <summary>
        ///     Sets the display output HWND.
        /// </summary>
        /// <param name="handle">The handle.</param>
        public void SetDisplayOutputHwnd(IntPtr handle)
        {
            LibVlcMediaPlayerInterop.libvlc_media_player_set_hwnd(Handle, handle);
        }

        /// <summary>
        ///     Sets the display output NS object.
        /// </summary>
        /// <param name="handle">The handle.</param>
        public void SetDisplayOutputNSObject(IntPtr handle)
        {
            LibVlcMediaPlayerInterop.libvlc_media_player_set_nsobject(Handle, handle);
        }

        /// <summary>
        ///     Sets the display output X window.
        /// </summary>
        /// <param name="handle">The handle.</param>
        public void SetDisplayOutputXWindow(IntPtr handle)
        {
            LibVlcMediaPlayerInterop.libvlc_media_player_set_xwindow(Handle, handle);
        }

        /// <summary>
        ///     Sets the display output agl.
        /// </summary>
        /// <param name="handle">The handle.</param>
        public void SetDisplayOutputAgl(IntPtr handle)
        {
            LibVlcMediaPlayerInterop.libvlc_media_player_set_agl(Handle, handle);
        }

        #endregion

        #region Operation

        /// <summary>
        ///     Plays this instance.
        /// </summary>
        /// <exception cref="VlcInternalException"></exception>
        public virtual void Play()
        {
            var res = LibVlcMediaPlayerInterop.libvlc_media_player_play(Handle);
            if (res != 0)
            {
                throw new VlcInternalException(LibVlcCoreInterop.libvlc_errmsg());
            }
        }

        /// <summary>
        ///     Plays the specified media input.
        /// </summary>
        /// <param name="mediaInput">The media input.</param>
        public abstract void Play(MediaInput mediaInput);

        /// <summary>
        /// Stops this instance.
        /// </summary>
        public virtual void Stop()
        {
            LibVlcMediaPlayerInterop.libvlc_media_player_stop(Handle);
        }

        /// <summary>
        /// Pauses this instance.
        /// </summary>
        public virtual void Pause()
        {
            LibVlcMediaPlayerInterop.libvlc_media_player_pause(Handle);
        }

        /// <summary>
        /// Resumes this instance.
        /// </summary>
        public virtual void Resume()
        {
        }

        /// <summary>
        /// Plays the next.
        /// </summary>
        public virtual void Forward()
        {
        }

        /// <summary>
        /// Backwards this instance.
        /// </summary>
        public virtual void Backward()
        {
        }

        #endregion

        #region Set Media

        /// <summary>
        ///     Parses the media input.
        /// </summary>
        /// <param name="mediaInput">The media input.</param>
        /// <returns>PreparsedMedia.</returns>
        public virtual BaseMedia ParseMediaInput(MediaInput mediaInput)
        {
            var media = LibVlcFactoryInstance.CreateLibVlcMedia(mediaInput);
            return media;
        }

        /// <summary>
        /// Sets the media input.
        /// </summary>
        /// <param name="mediaInput">The media input.</param>
        /// <exception cref="System.NotImplementedException"></exception>
        public virtual void SetMediaInput(MediaInput mediaInput)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Sets the media input.
        /// </summary>
        /// <param name="preparsedMedia">The preparsed media.</param>
        /// <exception cref="System.NotImplementedException"></exception>
        public virtual void SetMediaInput(BaseMedia preparsedMedia)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Sets the next media input.
        /// </summary>
        /// <param name="mediaInput">The media input.</param>
        /// <exception cref="System.NotImplementedException"></exception>
        public virtual void SetNextMediaInput(MediaInput mediaInput)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Sets the next media input.
        /// </summary>
        /// <param name="preparsedMedia">The preparsed media.</param>
        /// <exception cref="System.NotImplementedException"></exception>
        public virtual void SetNextMediaInput(BaseMedia preparsedMedia)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        ///     Sets the media.
        /// </summary>
        /// <param name="media">The media.</param>
        /// <exception cref="System.ArgumentNullException">media</exception>
        public void SetMedia(BaseMedia media)
        {
            if (media == null)
            {
                throw new ArgumentNullException("media");
            }
            //
            LibVlcMediaPlayerInterop.libvlc_media_player_set_media(Handle, media.Handle);
        }

        #endregion

        #region More Operation

        /// <summary>
        ///     Sets the video adjust.
        /// </summary>
        /// <param name="option">The option.</param>
        /// <param name="value">The value.</param>
        public void SetVideoAdjust(VideoAdjustOption option, object value)
        {
            if (Handle == IntPtr.Zero)
            {
                throw new MediaPlayerException("Player is empty.");
            }
            VideoTools.SetVideoAdjust(Handle, option, value);
        }

        /// <summary>
        ///     Gets the video adjust.
        /// </summary>
        /// <param name="option">The option.</param>
        /// <returns>System.Object.</returns>
        /// <exception cref="MediaPlayerException">Player is empty.</exception>
        public object GetVideoAdjust(VideoAdjustOption option)
        {
            if (Handle == IntPtr.Zero)
            {
                throw new MediaPlayerException("Player is empty.");
            }
            return VideoTools.GetVideoAdjust(Handle, option);
        }

        /// <summary>
        ///     Setvideoes the aspect ratio.
        /// </summary>
        /// <param name="videoAspectRatioType">Type of the video aspect ratio.</param>
        /// <exception cref="MediaPlayerException">Player is empty.</exception>
        public void SetvideoAspectRatio(VideoAspectRatioType videoAspectRatioType)
        {
            if (Handle == IntPtr.Zero)
            {
                throw new MediaPlayerException("Player is empty.");
            }
            LibVlcVideoInterop.libvlc_video_set_aspect_ratio(Handle, videoAspectRatioType);
        }

        /// <summary>
        ///     Getvideoes the aspect ratio.
        /// </summary>
        /// <returns>System.String.</returns>
        /// <exception cref="MediaPlayerException">Player is empty.</exception>
        public string GetvideoAspectRatio()
        {
            if (Handle == IntPtr.Zero)
            {
                throw new MediaPlayerException("Player is empty.");
            }
            var res = LibVlcVideoInterop.libvlc_video_get_aspect_ratio(Handle);
            return res ?? string.Empty;
        }

        /// <summary>
        ///     Gets the size of the video.
        /// </summary>
        /// <param name="width">The width.</param>
        /// <param name="height">The height.</param>
        /// <returns>
        ///     <c>true</c> if XXXX, <c>false</c> otherwise
        /// </returns>
        /// <exception cref="MediaPlayerException">Player is empty.</exception>
        /// <exception cref="System.Exception">the specified video does not exist.</exception>
        public bool GetVideoSize(out uint width, out uint height)
        {
            if (Handle == IntPtr.Zero)
            {
                throw new MediaPlayerException("Player is empty.");
            }
            //
            var res = LibVlcVideoInterop.libvlc_video_get_size(Handle, 0, out width, out height);
            switch (res)
            {
                case 0:
                    return true;
                case -1:
                    throw new Exception("the specified video does not exist.");
            }
            return false;
        }

        /// <summary>
        ///     Sets the video logo.
        /// </summary>
        /// <param name="option">The option.</param>
        /// <param name="vlaue">The vlaue.</param>
        public void SetVideoLogo(VideoLogoOption option, object vlaue)
        {
            if (Handle == IntPtr.Zero)
            {
                throw new MediaPlayerException("Player is empty.");
            }
            switch (option)
            {
                case VideoLogoOption.Delay:
                    LibVlcVideoInterop.libvlc_video_set_logo_int(Handle, option, (int)vlaue);
                    break;
                case VideoLogoOption.Enable:
                    LibVlcVideoInterop.libvlc_video_set_logo_int(Handle, option, (int)vlaue);
                    break;
                case VideoLogoOption.File:
                    if (!File.Exists(vlaue.ToString()))
                    {
                        throw new IOException("File dose not exist. " + vlaue);
                    }
                    LibVlcVideoInterop.libvlc_video_set_logo_string(Handle, option, (string)vlaue);
                    break;
                case VideoLogoOption.Opacity:
                    LibVlcVideoInterop.libvlc_video_set_logo_int(Handle, option, (int)vlaue);
                    break;
                case VideoLogoOption.Position:
                    LibVlcVideoInterop.libvlc_video_set_logo_int(Handle, option, (int)vlaue);
                    break;
                case VideoLogoOption.Repeat:
                    LibVlcVideoInterop.libvlc_video_set_logo_int(Handle, option, (int)vlaue);
                    break;
                case VideoLogoOption.X:
                    LibVlcVideoInterop.libvlc_video_set_logo_int(Handle, option, (int)vlaue);
                    break;
                case VideoLogoOption.Y:
                    LibVlcVideoInterop.libvlc_video_set_logo_int(Handle, option, (int)vlaue);
                    break;
            }
        }

        /// <summary>
        ///     Takes the snapshot of currently playing media. Snapshot will be stored in specified file.
        /// </summary>
        /// <param name="filePath">The file path.</param>
        /// <param name="width">The width.</param>
        /// <param name="height">The height.</param>
        /// <exception cref="MediaPlayerException">
        ///     Player is empty.
        ///     or
        ///     Unexpected player state.
        /// </exception>
        /// <exception cref="VlcInternalException"></exception>
        public void TakeSnapshot(string filePath, int width, int height)
        {
            if (Handle == IntPtr.Zero)
            {
                throw new MediaPlayerException("Player is empty.");
            }
            //
            if ((State != VlcPlayerState.Playing) &&
                (State != VlcPlayerState.Paused))
            {
                throw new MediaPlayerException("Unexpected player state.");
            }
            //
            var filePathPtr = Marshal.StringToHGlobalAnsi(filePath);
            //
            var uwidth = Convert.ToUInt32(width);
            var uheight = Convert.ToUInt32(height);
            //
            try
            {
                var res = LibVlcVideoInterop.libvlc_video_take_snapshot(CurrentMediaHandle, 0, filePathPtr, uwidth,
                                                                        uheight);
                if (-1 == res)
                {
                    throw new VlcInternalException(LibVlcCoreInterop.libvlc_errmsg());
                }
            }
            finally
            {
                Marshal.FreeHGlobal(filePathPtr);
            }
        }

        /// <summary>
        ///     Adds the media option.
        /// </summary>
        /// <param name="option">The option.</param>
        /// <exception cref="MediaPlayerException">Player is empty.</exception>
        /// <exception cref="System.ArgumentNullException">option</exception>
        /// <exception cref="System.ArgumentException">String is empty.;option</exception>
        public void AddMediaOption(string option)
        {
            CurrentMedia.AddOption(option);
        }

        /// <summary>
        ///     Gets the spu description.
        /// </summary>
        public void GetSpuDescription()
        {
            if (Handle == IntPtr.Zero)
            {
                throw new MediaPlayerException("Player is empty.");
            }
            LibVlcVideoInterop.libvlc_video_get_spu_description(CurrentMediaHandle);
        }

        #endregion

        #region Events

        /// <summary>
        /// Inits the events.
        /// </summary>
        protected void InitEvents()
        {
            if (Handle == IntPtr.Zero)
                return;
            _myEventManagerHandle = LibVlcMediaPlayerInterop.libvlc_media_player_event_manager(Handle);
            _myEventCallback = OnVlcEvent;
            _myEventCallbackHandle = GCHandle.Alloc(_myEventCallback);
            AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerMediaChanged);
            AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerNothingSpecial);
            AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerOpening);//
            AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerBuffering);//
            AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerPlaying);//
            AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerPaused);//
            AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerStopped);//
            //AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerForward);
            //AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerBackward);
            AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerEndReached);//
            AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerEncounteredError);//
            AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerTimeChanged);//
            AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerPositionChanged);//
            //注意不能回调libvlc_MediaPlayerSeekableChanged 会产生死锁，无法停止播放以及程序出现无影响的状态
            //如果要解决此问题，可以在停止播放之前移除事件的的绑定，即调用FreeEvents（）方法。然后播放的时候在绑定。
            // AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerSeekableChanged);//
           // AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerPausableChanged);//
            AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerTitleChanged);
            AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerSnapshotTaken);
            AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerLengthChanged);
            AttachToEvent(libvlc_event_type_e.libvlc_MediaPlayerVout);
        }
        /// <summary>
        /// Frees the events.
        /// </summary>
        protected void FreeEvents()
        {
            if (_myEventManagerHandle == IntPtr.Zero || _myEventCallback == null)
            {
                return;
            }
            DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerMediaChanged);
            DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerNothingSpecial);
            DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerOpening);
            DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerBuffering);
            DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerPlaying);
            DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerPaused);
            DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerStopped);
            //DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerForward);
            //DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerBackward);
            DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerEndReached);
            DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerEncounteredError);
            DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerTimeChanged);
            DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerPositionChanged);
            // DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerSeekableChanged);
            // DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerPausableChanged);
            DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerTitleChanged);
            DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerSnapshotTaken);
            DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerLengthChanged);
            DetachEvent(libvlc_event_type_e.libvlc_MediaPlayerVout);
            _myEventCallbackHandle.Free();
        }

        /// <summary>
        /// Raises the <see>
        ///                <cref>E:VlcEvent</cref>
        ///            </see>
        ///     event.
        /// </summary>
        /// <param name="libvlcEvent">The libvlc event.</param>
        /// <param name="userData">The user data.</param>
        [AllowReversePInvokeCalls]
        private void OnVlcEvent(IntPtr libvlcEvent, IntPtr userData)
        {
            var eventData = (libvlc_event_t)Marshal.PtrToStructure(libvlcEvent, typeof(libvlc_event_t));
            switch (eventData.type)
            {
                case libvlc_event_type_e.libvlc_MediaPlayerMediaChanged:
                    EventsHelper.RaiseEvent(MediaPlayerMediaChanged, this, new VlcEventArgs<IntPtr>(eventData.media_player_media_changed.new_media));
                    break;
                case libvlc_event_type_e.libvlc_MediaPlayerNothingSpecial:
                    EventsHelper.RaiseEvent(MediaPlayerStateChanged, this, new VlcEventArgs<VlcPlayerState>(VlcPlayerState.Idle));
                    break;
                case libvlc_event_type_e.libvlc_MediaPlayerOpening:
                    EventsHelper.RaiseEvent(MediaPlayerStateChanged, this, new VlcEventArgs<VlcPlayerState>(VlcPlayerState.Opening));
                    break;
                case libvlc_event_type_e.libvlc_MediaPlayerBuffering:
                    EventsHelper.RaiseEvent(MediaPlayerBuffering, this, new VlcEventArgs<float>(eventData.media_player_buffering.new_cache));
                    break;
                case libvlc_event_type_e.libvlc_MediaPlayerPlaying:
                    EventsHelper.RaiseEvent(MediaPlayerStateChanged, this, new VlcEventArgs<VlcPlayerState>(VlcPlayerState.Playing));
                    break;
                case libvlc_event_type_e.libvlc_MediaPlayerPaused:
                    EventsHelper.RaiseEvent(MediaPlayerStateChanged, this, new VlcEventArgs<VlcPlayerState>(VlcPlayerState.Paused));
                    break;
                case libvlc_event_type_e.libvlc_MediaPlayerStopped:
                    EventsHelper.RaiseEvent(MediaPlayerStateChanged, this, new VlcEventArgs<VlcPlayerState>(VlcPlayerState.Idle));
                    break;
                //case libvlc_event_type_e.libvlc_MediaPlayerForward:
                //    EventsHelper.RaiseEvent(MediaPlayerStateChanged, this, new VlcEventArgs<VlcPlayerState>(VlcPlayerState.Forward));
                //    break;
                //case libvlc_event_type_e.libvlc_MediaPlayerBackward:
                //    EventsHelper.RaiseEvent(MediaPlayerStateChanged, this, new VlcEventArgs<VlcPlayerState>(VlcPlayerState.Backward));
                //    break;
                case libvlc_event_type_e.libvlc_MediaPlayerEndReached:
                    EventsHelper.RaiseEvent(MediaPlayerStateChanged, this, new VlcEventArgs<VlcPlayerState>(VlcPlayerState.Ended));
                    break;
                case libvlc_event_type_e.libvlc_MediaPlayerEncounteredError:
                    EventsHelper.RaiseEvent(MediaPlayerStateChanged, this, new VlcEventArgs<VlcPlayerState>(VlcPlayerState.Error));
                    break;
                case libvlc_event_type_e.libvlc_MediaPlayerTimeChanged:
                    EventsHelper.RaiseEvent(MediaPlayerTimeChanged, this, new VlcEventArgs<long>(eventData.media_player_time_changed.new_time));
                    break;
                case libvlc_event_type_e.libvlc_MediaPlayerPositionChanged:
                    EventsHelper.RaiseEvent(MediaPlayerPositionChanged, this, new VlcEventArgs<float>(eventData.media_player_position_changed.new_position));
                    break;
                //case libvlc_event_type_e.libvlc_MediaPlayerSeekableChanged:
                //    EventsHelper.RaiseEvent(MediaPlayerSeekableChanged, this, new VlcEventArgs<int>(eventData.media_player_seekable_changed.new_seekable));
                //    break;
                //case libvlc_event_type_e.libvlc_MediaPlayerPausableChanged:
                  //  EventsHelper.RaiseEvent(MediaPlayerPausableChanged, this, new VlcEventArgs<int>(eventData.media_player_pausable_changed.new_pausable));
                  //  break;
                case libvlc_event_type_e.libvlc_MediaPlayerTitleChanged:
                    EventsHelper.RaiseEvent(MediaPlayerTitleChanged, this, new VlcEventArgs<int>(eventData.media_player_title_changed.new_title));
                    break;
                case libvlc_event_type_e.libvlc_MediaPlayerSnapshotTaken:
                    EventsHelper.RaiseEvent(MediaPlayerSnapshotTaken, this, new VlcEventArgs<IntPtr>(eventData.media_player_snapshot_taken.psz_filename));
                    break;
                case libvlc_event_type_e.libvlc_MediaPlayerLengthChanged:
                    EventsHelper.RaiseEvent(MediaPlayerLengthChanged, this, new VlcEventArgs<long>(eventData.media_player_length_changed.new_length));
                    break;
                case libvlc_event_type_e.libvlc_MediaPlayerVout:
                    EventsHelper.RaiseEvent(MediaPlayerVout, this, new VlcEventArgs<int>(eventData.media_player_vout.new_count));
                    break;
            }
        }

        /// <summary>
        /// Occurs when [media player media changed].
        /// </summary>
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, IntPtr> MediaPlayerMediaChanged;
        //[Category("RuanRang.com VideoLan Event")]
        //public event VlcEventHandler<BaseMediaPlayer, IntPtr> MediaPlayerNothingSpecial;
        [Category("RuanRang.com VideoLan Event")]
        //public event VlcEventHandler<BaseMediaPlayer, IntPtr> MediaPlayerOpening;
        //[Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, float> MediaPlayerBuffering;
        //[Category("RuanRang.com VideoLan Event")]
        //public event VlcEventHandler<BaseMediaPlayer, bool> MediaPlayerPlaying;
        //[Category("RuanRang.com VideoLan Event")]
        //public event VlcEventHandler<BaseMediaPlayer, bool> MediaPlayerPaused;
        //[Category("RuanRang.com VideoLan Event")]
        //public event VlcEventHandler<BaseMediaPlayer, bool> MediaPlayerStopped;
        //[Category("RuanRang.com VideoLan Event")]
        //public event VlcEventHandler<BaseMediaPlayer, bool> MediaPlayerForward;
        //[Category("RuanRang.com VideoLan Event")]
        //public event VlcEventHandler<BaseMediaPlayer, bool> MediaPlayerBackward;
        //[Category("RuanRang.com VideoLan Event")]
        //public event VlcEventHandler<BaseMediaPlayer, IntPtr> MediaPlayerEndReached;
        //[Category("RuanRang.com VideoLan Event")]
        //public event VlcEventHandler<BaseMediaPlayer, IntPtr> MediaPlayerEncounteredError;
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, long> MediaPlayerTimeChanged;
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, float> MediaPlayerPositionChanged;
        //[Category("RuanRang.com VideoLan Event")]
        //public event VlcEventHandler<BaseMediaPlayer, int> MediaPlayerSeekableChanged;
        //[Category("RuanRang.com VideoLan Event")]
        //public event VlcEventHandler<BaseMediaPlayer, int> MediaPlayerPausableChanged;
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, int> MediaPlayerTitleChanged;
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, IntPtr> MediaPlayerSnapshotTaken;
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, long> MediaPlayerLengthChanged;
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, int> MediaPlayerVout;
        //
        /// <summary>
        /// Occurs when [media player state changed]. 非LIBVLC内部事件
        /// </summary>
        [Category("RuanRang.com VideoLan Event")]
        public event VlcEventHandler<BaseMediaPlayer, VlcPlayerState> MediaPlayerStateChanged;
        //
        /// <summary>
        /// Attaches an event to player with selected eventManager.
        /// And stores used delegate in internal list.
        /// </summary>
        /// <param name="eventType">Type of the event.</param>
        /// <exception cref="System.ArgumentException">IntPtr.Zero is invalid value.;eventManager</exception>
        /// <exception cref="VlcInternalException"></exception>
        internal void AttachToEvent(libvlc_event_type_e eventType)
        {
            if (_myEventManagerHandle == IntPtr.Zero)
            {
                throw new ArgumentException("IntPtr.Zero is invalid value.", "eventType");
            }
            var res = LibVlcCoreInterop.libvlc_event_attach(_myEventManagerHandle, eventType, _myEventCallback, IntPtr.Zero);
            if (res != 0)
            {
#if DEBUG
                Debug.WriteLine(String.Format("Unable to attach event {0}", eventType));
#else
                throw new VlcInternalException(String.Format("Unable to attach event {0}", eventType));
#endif
            }
        }

        /// <summary>
        /// Detaches events attached previously.
        /// </summary>
        /// <param name="eventType">Type of the event.</param>
        internal void DetachEvent(libvlc_event_type_e eventType)
        {
            LibVlcCoreInterop.libvlc_event_detach(_myEventManagerHandle, eventType, _myEventCallback, IntPtr.Zero);
        }

        #endregion

        #region Properity
        /// <summary>
        ///     Gets or sets the current media input.
        /// </summary>
        /// <value>The current media input.</value>
        public MediaInput CurrentMediaInput { get; set; }

        /// <summary>
        ///     Gets or sets the media player handle.
        /// </summary>
        /// <value>The media player handle.</value>
        public IntPtr Handle { get; set; }

        /// <summary>
        ///     Gets or sets the current media handle.
        /// </summary>
        /// <value>The current media handle.</value>
        public IntPtr CurrentMediaHandle { get; set; }

        /// <summary>
        ///     Gets or sets the player output.
        /// </summary>
        /// <value>The player output.</value>
        public PlayerOutput PlayerOutput { get; set; }

        /// <summary>
        ///     Gets or sets the lib VLC factory instance.
        /// </summary>
        /// <value>The lib VLC factory instance.</value>
        public ILibVlcFactory LibVlcFactoryInstance { get; set; }

        // this will be set from Play/PlayNext methods. this is actually playing media
        /// <summary>
        ///     Gets or sets the current media.
        /// </summary>
        /// <value>
        ///     The current media.
        /// </value>
        public BaseMedia CurrentMedia { get; set; }

        // we do not control VlcMediaInternal in case if it was preparsed.

        /// <summary>
        ///     Gets or sets the SPU.
        /// </summary>
        /// <value>The SPU.</value>
        /// <exception cref="VlcInternalException"></exception>
        public int SPU
        {
            get
            {
                var res = LibVlcVideoInterop.libvlc_video_get_spu(Handle);
                //
                return (res);
            }
            set
            {
                var res = LibVlcVideoInterop.libvlc_video_set_spu(Handle, value);
                if (-1 == res)
                {
                    throw new VlcInternalException(LibVlcCoreInterop.libvlc_errmsg());
                }
            }
        }

        /// <summary>
        ///     Frames per second.
        /// </summary>
        /// <value>The FPS.</value>
        public float FPS
        {
            get
            {
                var res = LibVlcMediaPlayerInterop.libvlc_media_player_get_fps(Handle);
                return (res);
            }
        }

        /// <summary>
        ///     Gets the length.
        /// </summary>
        /// <value>The length.</value>
        /// <exception cref="VlcInternalException"></exception>
        public TimeSpan Length
        {
            get
            {
                var res = LibVlcMediaPlayerInterop.libvlc_media_player_get_length(Handle);
                if (-1 == res)
                {
                    throw new VlcInternalException(LibVlcCoreInterop.libvlc_errmsg());
                }
                //
                return new TimeSpan(res * 10000);
            }
        }

        /// <summary>
        ///     Gets the state.
        /// </summary>
        /// <value>The state.</value>
        public VlcPlayerState State { 
            get
            {
                var state = (VlcPlayerState)LibVlcMediaPlayerInterop.libvlc_media_player_get_state(Handle);
                return state;
            }
        }

        /// <summary>
        ///     Gets or sets the position.
        ///     Get movie position as percentage between 0.0 and 1.0.
        /// </summary>
        /// <value>The position.</value>
        public float Position
        {
            get
            {
                if ((State != VlcPlayerState.Paused) && (State != VlcPlayerState.Playing))
                {
                    return 0f;
                }
                var res = LibVlcMediaPlayerInterop.libvlc_media_player_get_position(Handle);
                if (Math.Abs(-1.0 - res) <= 0x00)
                {
                    throw new VlcInternalException(LibVlcCoreInterop.libvlc_errmsg());
                }
                //
                return (res);
            }
            set
            {
                if ((State != VlcPlayerState.Paused) && (State != VlcPlayerState.Playing))
                {
                    return;
                }
                if (Handle == null)
                {
                    throw new MediaPlayerException("Current media is not loaded.");
                }
                if ((value < 0f) || (value > 1.0f))
                {
                    throw new ArgumentOutOfRangeException("value", "must be (0.0 - 1.0)");
                }
                LibVlcMediaPlayerInterop.libvlc_media_player_set_position(Handle, value);
            }
        }

        /// <summary>
        ///     Gets or sets a value indicating whether this instance is full screen.
        /// </summary>
        /// <value>
        ///     <c>true</c> if this instance is full screen; otherwise, <c>false</c>.
        /// </value>
        public bool IsFullScreen
        {
            get
            {
                var fullscreen = LibVlcVideoInterop.libvlc_get_fullscreen(Handle);
                return fullscreen == 1;
            }
            set { LibVlcVideoInterop.libvlc_set_fullscreen(Handle, value ? 1 : 0); }
        }

        /// <summary>
        ///     Gets or sets the time.
        /// </summary>
        /// <value>The time.</value>
        public TimeSpan Time
        {
            get
            {
                if ((State != VlcPlayerState.Paused) && (State != VlcPlayerState.Playing))
                {
                    return (TimeSpan.Zero);
                }
                var res = LibVlcMediaPlayerInterop.libvlc_media_player_get_time(Handle);
                if (res == -1)
                {
                    throw new VlcInternalException(LibVlcCoreInterop.libvlc_errmsg());
                }
                //
                return new TimeSpan(res * 10000);
            }
            set
            {

                if ((State != VlcPlayerState.Playing) && (State != VlcPlayerState.Paused))
                {
                    return;
                }
                LibVlcMediaPlayerInterop.libvlc_media_player_set_time(Handle,
                                                                      Convert.ToInt64(value.TotalMilliseconds));
            }
        }

        /// <summary>
        ///     Gets or sets the volume.
        /// </summary>
        /// <value>The volume.</value>
        public int Volume
        {
            get
            {
                var res = LibVlcAudioInterop.libvlc_audio_get_volume(Handle);
                return (res);
            }
            set
            {
                if ((value > 200) || (value < 0))
                {
                    throw new ArgumentException("Must be between 0 and 200.", "value");
                }
                var res = LibVlcAudioInterop.libvlc_audio_set_volume(Handle, value);
                if (res != 0)
                {
                    throw new VlcInternalException(LibVlcCoreInterop.libvlc_errmsg());
                }
            }
        }
        #endregion

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        // NOTE: Leave out the finalizer altogether if this class doesn't 
        // own unmanaged resources itself, but leave the other methods
        // exactly as they are. 
        /// <summary>
        /// Finalizes an instance of the <see cref="BaseMediaPlayer" /> class.
        /// </summary>
        ~BaseMediaPlayer()
        {
            // Finalizer calls Dispose(false)
            Dispose(false);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources.
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                //free managed resources

            }
            // unmanaged managed resources
            //注意在调试的时候 如果这里此时如果是播放中的状态，这把STOP下断点会导致死锁的问题
            //因为此时调试器堵塞当前线程，而后台的非托管线程还在运行, 且非托管线程还在回调当前的线程上的方法。
            Stop();
            FreeEvents();
            LibVlcMediaPlayerInterop.libvlc_media_player_release(Handle);
            IntropHandleManager.LibVlcPlayerHandle = IntPtr.Zero;
        }
    }
}